/**
 *    Copyright (C) 2008, 2009  Victor Smirnov <aist11@gmail.com>
 *
 *    This program is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License along
 *    with this program; if not, write to the Free Software Foundation, Inc.,
 *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 *    See LICENSE.exceptions in the root folder of this project for possible
 *    exceptions
 *
 */

#include <decls.hpp>
#include <exceptions.hpp>
#include <natives.h>
#include <notifier.hpp>

JNIEXPORT jboolean JNICALL METADATA1(Typed, IsInstance) (JNIEnv *env, jclass, jlong me, jlong target)
{
    CUBEFS_EXC_BEGIN

    Typed* typed = Handle2Ptr<Typed>(me);
    Type*  type  = Handle2Ptr<Type>(target);
    return typed->IsInstance(type);

    CUBEFS_EXC_HANDLE
    return false;
}

JNIEXPORT jboolean JNICALL METADATA1(Typed, managed) (JNIEnv *env, jclass, jlong me)
{
    CUBEFS_EXC_BEGIN

    Typed* typed = Handle2Ptr<Typed>(me);
    return typed->managed();

    CUBEFS_EXC_HANDLE
    return false;
}

JNIEXPORT jlong JNICALL METADATA1(Typed, typeHandle) (JNIEnv *env, jclass, jlong me)
{
    CUBEFS_EXC_BEGIN
    Typed* typed = Handle2Ptr<Typed>(me);
    return Ptr2Handle(typed->type());
    CUBEFS_EXC_HANDLE
    return 0;
}

JNIEXPORT void JNICALL METADATA1(Typed, destroy) (JNIEnv *env, jclass, jlong me)
{
    CUBEFS_EXC_BEGIN

    Typed* typed = Handle2Ptr<Typed>(me);
    if (!typed->managed()) {
        //cout<<"Delete object of type "<<typed->type()->name()<<endl;
        delete typed;
    }
    else {
        //cout<<"Cleanup peer for object of type "<<typed->type()->name()<<endl;
        typed->CleanupPeer();
    }

    CUBEFS_EXC_HANDLE
}


JNIEXPORT jobject JNICALL METADATA1(Typed, GetPeer) (JNIEnv *env, jclass, jlong me)
{
    CUBEFS_EXC_BEGIN

    Typed* typed = Handle2Ptr<Typed>(me);
    return (jobject)typed->GetPeer();
    
    CUBEFS_EXC_HANDLE
    return NULL;
}

JNIEXPORT void JNICALL METADATA1(Typed, SetPeer) (JNIEnv *env, jobject obj, jlong me, jboolean weak)
{
    CUBEFS_EXC_BEGIN

    Typed* typed = Handle2Ptr<Typed>(me);

    if (typed->GetPeer() != 0) {
        if (typed->GetPeerFlags() & WEAK_PEER) {
            jweak peer = (jweak)typed->GetPeer();
            env->DeleteWeakGlobalRef(peer);
        }
        else {
            jobject peer = (jobject)typed->GetPeer();
            env->DeleteGlobalRef(peer);
        }
    }

    if (weak) {
        typed->SetPeer(Ptr2Handle(env->NewWeakGlobalRef(obj)));
        typed->SetPeerFlags(WEAK_PEER);
    }
    else {
        typed->SetPeer(Ptr2Handle(env->NewGlobalRef(obj)));
        typed->SetPeerFlags(0);
    }

    CUBEFS_EXC_HANDLE
}

JNIEXPORT void JNICALL METADATA1(Typed, initNotifier) (JNIEnv *env, jclass typed)
{
    CUBEFS_EXC_BEGIN

    if (env->GetJavaVM(&currentVM) < 0) {
        return;
    }
    else {
        notifyMID = env->GetMethodID(typed, "notify", "(I)V");
        if (notifyMID != NULL) {
            Typed::SetPeerNotifier(&NotifyTypedObject);
            Typed::SetPeerCleanup(&CleanupPeer);
        }
        else {
            Typed::SetPeerNotifier(NULL);
        }
    }

    CUBEFS_EXC_HANDLE
}

JNIEXPORT void JNICALL METADATA1(Typed, notifyTest) (JNIEnv *env, jclass typed, jlong handle, jint signal)
{
    CUBEFS_EXC_BEGIN

    Typed *target = Handle2Ptr<Typed>(handle);

    target->notify(signal);

    CUBEFS_EXC_HANDLE
}

JNIEXPORT jlong JNICALL METADATA1(Type, type) (JNIEnv *env, jclass, jlong handle)
{
    CUBEFS_EXC_BEGIN
    Type *type = Handle2Ptr<Type>(handle);
    return Ptr2Handle(type->type());

    CUBEFS_EXC_HANDLE
    return 0;
}

JNIEXPORT jstring JNICALL METADATA1(Type, name) (JNIEnv *env, jclass, jlong handle)
{
    CUBEFS_EXC_BEGIN

    Type *type = Handle2Ptr<Type>(handle);
    return env->NewStringUTF(type->name().c_str());

    CUBEFS_EXC_HANDLE
    return NULL;
}

JNIEXPORT jlong JNICALL METADATA1(Type, parent) (JNIEnv *env, jclass, jlong handle)
{
    CUBEFS_EXC_BEGIN

    Type *type = Handle2Ptr<Type>(handle);
    return Ptr2Handle(type->parent());

    CUBEFS_EXC_HANDLE
    return 0;
}

JNIEXPORT jint JNICALL METADATA1(Type, GetOperationsCount) (JNIEnv *env, jclass, jlong handle)
{
    CUBEFS_EXC_BEGIN

    Type *type = Handle2Ptr<Type>(handle);
    return type->GetOperationsCount();

    CUBEFS_EXC_HANDLE
    return 0;
}

JNIEXPORT jlong JNICALL METADATA1(Type, GetOperation) (JNIEnv *env, jclass, jlong handle, jint idx)
{
    CUBEFS_EXC_BEGIN

    Type *type = Handle2Ptr<Type>(handle);
    return Ptr2Handle(type->GetOperation(idx));

    CUBEFS_EXC_HANDLE
    return 0;
}

JNIEXPORT jlong JNICALL METADATA1(Type, FindOperation) (JNIEnv *env, jclass, jlong handle, jstring name, jlong args)
{
    CUBEFS_EXC_BEGIN

    Type *type = Handle2Ptr<Type>(handle);
    ListOfTypes* arguments = Handle2Ptr<ListOfTypes>(args, false);

    const char *str = env->GetStringUTFChars(name, NULL);
    if (str == NULL) {
        return 0; /* OutOfMemoryError already thrown */
    }

    Operation* op = type->FindOperation(str, arguments);

    env->ReleaseStringUTFChars(name, str);

    return Ptr2Handle(op);

    CUBEFS_EXC_HANDLE
    return 0;
}


JNIEXPORT jboolean JNICALL METADATA1(Type, IsSameOrSubclass) (JNIEnv *env, jclass, jlong me, jlong other)
{
    CUBEFS_EXC_BEGIN

    Type *myType = Handle2Ptr<Type>(me);
    Type *otherType = Handle2Ptr<Type>(other);

    return myType->IsSameOrSubclass(otherType);

    CUBEFS_EXC_HANDLE
    return false;
}





JNIEXPORT jlong JNICALL METADATA1(Types, intNumberType) (JNIEnv *env, jclass)
{
    CUBEFS_EXC_BEGIN

    return Ptr2Handle(TypedNumber<Int>::MyType());

    CUBEFS_EXC_HANDLE
    return 0;
}

JNIEXPORT jlong JNICALL METADATA1(Types, longNumberType) (JNIEnv *env, jclass)
{
    CUBEFS_EXC_BEGIN

    return Ptr2Handle(TypedNumber<BigInt>::MyType());

    CUBEFS_EXC_HANDLE
    return 0;
}

JNIEXPORT jlong JNICALL METADATA1(Types, charNumberType) (JNIEnv *env, jclass)
{
    CUBEFS_EXC_BEGIN

    return Ptr2Handle(TypedNumber<UShort>::MyType());

    CUBEFS_EXC_HANDLE
    return 0;
}

JNIEXPORT jlong JNICALL METADATA1(Types, boolNumberType) (JNIEnv *env, jclass)
{
    CUBEFS_EXC_BEGIN

    return Ptr2Handle(TypedNumber<bool>::MyType());

    CUBEFS_EXC_HANDLE
    return 0;
}


JNIEXPORT jlong JNICALL METADATA1(Types, floatNumberType) (JNIEnv *env, jclass)
{
    CUBEFS_EXC_BEGIN

    return Ptr2Handle(TypedNumber<float>::MyType());

    CUBEFS_EXC_HANDLE
    return 0;
}

JNIEXPORT jlong JNICALL METADATA1(Types, doubleNumberType) (JNIEnv *env, jclass)
{
    CUBEFS_EXC_BEGIN

    return Ptr2Handle(TypedNumber<double>::MyType());

    CUBEFS_EXC_HANDLE
    return 0;
}

JNIEXPORT jlong JNICALL METADATA1(Types, byteNumberType) (JNIEnv *env, jclass)
{
    CUBEFS_EXC_BEGIN

    return Ptr2Handle(TypedNumber<Byte>::MyType());

    CUBEFS_EXC_HANDLE
    return 0;
}

JNIEXPORT jlong JNICALL METADATA1(Types, shortNumberType) (JNIEnv *env, jclass)
{
    CUBEFS_EXC_BEGIN

    return Ptr2Handle(TypedNumber<Short>::MyType());

    CUBEFS_EXC_HANDLE
    return 0;
}

JNIEXPORT jlong JNICALL METADATA1(Types, numberType) (JNIEnv *env, jclass)
{
    CUBEFS_EXC_BEGIN

    return Ptr2Handle(Number::MyType());

    CUBEFS_EXC_HANDLE
    return 0;
}



JNIEXPORT jlong JNICALL METADATA1(Types, stringType) (JNIEnv *env, jclass)
{
    CUBEFS_EXC_BEGIN

    return Ptr2Handle(TypedValue<String>::MyType());

    CUBEFS_EXC_HANDLE
    return 0;
}

JNIEXPORT jlong JNICALL METADATA1(Types, stringRefType) (JNIEnv *env, jclass)
{
    CUBEFS_EXC_BEGIN

    return Ptr2Handle(ConstTypedValue<StringRef>::MyType());

    CUBEFS_EXC_HANDLE
    return 0;
}

JNIEXPORT jlong JNICALL METADATA1(Types, voidPtrType) (JNIEnv *env, jclass)
{
    CUBEFS_EXC_BEGIN

    return Ptr2Handle(TypedValue<void*>::MyType());

    CUBEFS_EXC_HANDLE
    return 0;
}



JNIEXPORT jlong JNICALL METADATA1(Types, typeType) (JNIEnv *env, jclass)
{
    CUBEFS_EXC_BEGIN

    return Ptr2Handle(Type::MyType());

    CUBEFS_EXC_HANDLE
    return 0;
}

JNIEXPORT jlong JNICALL METADATA1(Types, typedType) (JNIEnv *env, jclass)
{
    CUBEFS_EXC_BEGIN

    return Ptr2Handle(Typed::MyType());

    CUBEFS_EXC_HANDLE
    return 0;
}

JNIEXPORT jlong JNICALL METADATA1(Types, operationType) (JNIEnv *env, jclass)
{
    CUBEFS_EXC_BEGIN

    return Ptr2Handle(Operation::MyType());

    CUBEFS_EXC_HANDLE
    return 0;
}